// -----------------------------
// Author: 		Dhaenens Gert	
// Creation: 	10/22/2014 3:45:33 PM				
// -----------------------------

#include "FileManager.h"

FileManager* FileManager::s_lpInstance = nullptr;
Bool FileManager::s_IsCleaned = false;

TString MakePath(const TString& path)
{
	const size_t len = MAX_PATH;
	TChar buffer[len];

	GetFullPathName(path.c_str(), len, buffer, nullptr);
	return TString(buffer);
}

void FileManager::AtPanic()
{
	if (s_lpInstance)
		s_lpInstance->Clean();
}

FileManager::FileManager()
{
	s_lpInstance = this;
}

FileManager::~FileManager()
{
}

void FileManager::Clean()
{
	if (s_IsCleaned)
		return;

	// Delete the Libraries
	for (auto lib : m_Libraries)
		UnloadLibrary(lib);
	// Delete the Resources
	for (auto pair : m_Resources)
		UnloadResource(pair.second);

	// Delete the Data Directory
	RemoveDirectory(MakePath(_T("Data")).c_str());

	s_IsCleaned = true;
}

Bool FileManager::Init()
{
	// Create the Data Directory
	CreateDirectory(MakePath(_T("Data")).c_str(), NULL);

	return true;
}

void FileManager::UnpackLibrary(WORD resourceId, const TString& FileName)
{
	Log::WriteLine(_T("[FILES]\t\tUnpacking Library \"%s\""), FileName.c_str());
	ExtractResource(resourceId, FileName, _T("DLL"));
	m_Libraries.push_back(FileName);
}
void FileManager::UnpackResource(WORD resourceId, const TString& ResourceType, const TString& Identifier)
{
	// Request a Unique Filename
	TString fileName = GetUniqueFileName();

	Log::WriteLine(_T("[FILES]\t\tUnpacking Resource \"%s\" (\"%s\")"), Identifier.c_str(), ResourceType.c_str());

	// Extract the Resource
	ExtractResource(resourceId, fileName.c_str(), ResourceType);

	// Save the Resource
	AddResource(Identifier, fileName);
}

void FileManager::AddResource(const TString& Identifier, const TString& File)
{
	m_Resources.insert(make_pair(Identifier, File));
}

TString FileManager::GetUniqueFileName() const
{
	TChar fileBuffer[MAX_PATH];
	GetTempFileName(MakePath(_T("Data")).c_str(), _T("tmp"), 0, fileBuffer);
	return TString(fileBuffer);
}
TString	FileManager::GetFileName(const TString& Identifier) const
{
	return m_Resources.at(Identifier);
}

/* Extract Resource Function */
void FileManager::ExtractResource(WORD resourceId, const TString& FileName, const TString& ResourceType)
{
	HINSTANCE hInstance = GetModuleHandle(NULL);

	// Find and load the resource
	HRSRC hResource = FindResource(hInstance, MAKEINTRESOURCE(resourceId), ResourceType.c_str());
	HGLOBAL hFileResource = LoadResource(hInstance, hResource);

	// Open and map this to a disk file
	LPVOID lpFile = LockResource(hFileResource);
	DWORD dwSize = SizeofResource(hInstance, hResource);

	// Open the file and filemap
	HANDLE hFile = CreateFile(FileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, dwSize, NULL);
	LPVOID lpAddress = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0);

	// Write the file
	CopyMemory(lpAddress, lpFile, dwSize);

	// Un-map the file and close the handles
	UnmapViewOfFile(lpAddress);
	CloseHandle(hFileMap);
	CloseHandle(hFile);
}
void FileManager::UnloadLibrary(const TString& FileName)
{
	HMODULE hModule = GetModuleHandle(FileName.c_str());
	FreeLibrary(hModule);

	UnloadResource(FileName);
}
void FileManager::UnloadResource(const TString& FileName)
{
	DeleteFile(FileName.c_str());
}